package com.restkeeper.service;

import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.expr.SQLQueryExpr;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.alibaba.druid.sql.parser.ParserException;
import com.alibaba.druid.sql.parser.SQLExprParser;
import com.alibaba.druid.sql.parser.Token;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.restkeeper.IDishSearchService;
import com.restkeeper.entity.DishEs;
import com.restkeeper.entity.SearchResult;
import com.restkeeper.exception.BussinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.nlpcn.es4sql.domain.Where;
import org.nlpcn.es4sql.exception.SqlParseException;
import org.nlpcn.es4sql.parse.ElasticSqlExprParser;
import org.nlpcn.es4sql.parse.SqlParser;
import org.nlpcn.es4sql.parse.WhereParser;
import org.nlpcn.es4sql.query.maker.QueryMaker;
import org.springframework.beans.factory.annotation.Value;

import java.io.IOException;
import java.util.List;
import java.util.Map;

@Service(version = "1.0.0", protocol = "dubbo")
@Slf4j
public class SearchServiceImp implements IDishSearchService{

    @Value("${es.host}")
    private String host;

    @Value("${es.port}")
    private int port;

    @Override
    public SearchResult<DishEs> searchAllByCode(String code, int type, int pageNo, int pageSize) throws Exception {
        String shopId = RpcContext.getContext().getAttachment("shopId");
        if(StringUtils.isEmpty(shopId)){
            throw new BussinessException("商户号不存在");
        }
        String storeId= RpcContext.getContext().getAttachment("storeId");
        if(StringUtils.isEmpty(storeId)){
            throw new BussinessException("门店不存在");
        }
        return queryIndexContent("dish","code like '%"+code+"%' and type="+type+" and is_deleted=0 and shop_id ='"+shopId+"' and store_id ='"+storeId+"' order by last_update_time desc",
                                        pageNo,
                                        pageSize);
    }

    @Override
    public SearchResult<DishEs> searchDishByCode(String code, int pageNo, int pageSize) throws Exception {
        String shopId = RpcContext.getContext().getAttachment("shopId");
        if(StringUtils.isEmpty(shopId)){
            throw new BussinessException("商户号不存在");
        }
        String storeId= RpcContext.getContext().getAttachment("storeId");
        if(StringUtils.isEmpty(storeId)){
            throw new BussinessException("门店不存在");
        }
        return queryIndexContent("dish","code like '%"+code+"%' and is_deleted=0 and shop_id ='"+shopId+"' and store_id ='"+storeId+"' order by last_update_time desc",
                pageNo,
                pageSize);
    }

    @Override
    public SearchResult<DishEs> searchDishByName(String name,int type,int pageNo,int pageSize) throws Exception {
        String shopId = RpcContext.getContext().getAttachment("shopId");
        if(StringUtils.isEmpty(shopId)){
            throw new BussinessException("商户号不存在");
        }
        String storeId= RpcContext.getContext().getAttachment("storeId");
        if(StringUtils.isEmpty(storeId)){
            throw new BussinessException("门店不存在");
        }
        return queryIndexContent("dish","dish_name like '%"+name+"%' and type="+type+" and is_deleted=0 and shop_id ='"+shopId+"' and store_id ='"+storeId+"' order by last_update_time desc",
                                                pageNo,
                                                pageSize);
    }


    /**
     * 查询指定索引下的信息
     * @param indexName 索引名称
     * @param condition 查询条件
     */
    private SearchResult<DishEs> queryIndexContent(String indexName, String condition,int pageNo,int pageSize) throws IOException {
        //实例化查询请求对象
        SearchRequest request = new SearchRequest(indexName);
        //构建查询客户端
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost(host, port, "http")));//初始化
        //实例化SearchSourceBuilder
        SearchSourceBuilder searchBuilder = new SearchSourceBuilder();
        //根据索引、查询条件构建查询构造器
        BoolQueryBuilder boolQueryBuilder = createQueryBuilderByWhere(indexName,condition);

        //将查询构造器注入SearchSourceBuilder
        searchBuilder.query(boolQueryBuilder);
        //searchBuilder.sort("last_update_time", SortOrder.DESC);
        searchBuilder.from(pageNo);
        searchBuilder.size(pageSize);
        searchBuilder.trackTotalHits(true);

        //设置请求查询的索引（查询构造器中已指定，无需重复设置）

        //将构建好的SearchSourceBuilder注入请求
        request.source(searchBuilder);

        //带入请求执行查询
        SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
        //得到查询结果
        SearchHits hits = searchResponse.getHits();

        SearchHit[] searchHits = hits.getHits();

        List<DishEs> listData = Lists.newArrayList();
        //遍历查询结果
        for(SearchHit hit : searchHits){
            Map<String,Object> datas = hit.getSourceAsMap();
            String jsonMap= JSON.toJSONString(datas);
            DishEs dish = JSON.parseObject(jsonMap,DishEs.class);
            listData.add(dish);
        }
        //关闭客户端连接
        client.close();

        SearchResult<DishEs> result = new SearchResult<>();
        result.setRecords(listData);
        result.setTotal(searchResponse.getHits().getTotalHits().value);

        return result;
    }


    /**
     * 构建查询构造器
     * @param indexName  索引名
     * @param whereExpress  查询条件:(f1=2 and f2=1) or (f3=1 and f4=1)
     * @return
     */
    public static BoolQueryBuilder createQueryBuilderByWhere(String indexName, String whereExpress) {
        BoolQueryBuilder boolQuery = null;

        try {
            String sql = "select * from " + indexName;
            String whereTemp = "";
            if (!Strings.isNullOrEmpty(whereExpress)) {
                whereTemp = " where 1=1 and " + whereExpress;
            }
            SQLQueryExpr sqlExpr = (SQLQueryExpr) toSqlExpr(sql + whereTemp);
            SqlParser sqlParser = new SqlParser();
            MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) sqlExpr.getSubQuery().getQuery();
            WhereParser whereParser = new WhereParser(sqlParser, query);
            Where where = whereParser.findWhere();
            if (where != null) {
                boolQuery = QueryMaker.explan(where);
            }
        } catch (SqlParseException e) {
            log.info("ReadES.createQueryBuilderByExpress-Exception,"+e.getMessage());
        }
        return boolQuery;
    }

    /**
     * 验证sql
     *
     * @param sql sql查询语句
     * @return and (a=1 and b=1) or (c=1 and d=1)
     */
    private static SQLExpr toSqlExpr(String sql) {
        SQLExprParser parser = new ElasticSqlExprParser(sql);
        SQLExpr expr = parser.expr();

        if (parser.getLexer().token() != Token.EOF) {
            throw new ParserException("illegal sql expr : " + sql);
        }
        return expr;
    }
}
